package org.abettor.pushbox.view;

import org.abettor.pushbox.map.Boxmap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
 * 显示推箱子的图片的类
 * 
 * @author jzh
 * 
 */
public abstract class BoxView extends ImageView {
	protected Bitmap box, wall, boxOnDest, manDown, manLeft, manRight, manUp,
			dest, backGround;// 各种资源图片
	protected Bitmap mPushBoxImage;// 推动箱子的背景
	protected Boxmap map;// 需要推动的地图
	protected float clipWidth, clipHeight;// 碎片的宽高
	protected int showWidth, showHeight;// 要显示的图片的大小
	protected Matrix mBaseMatrix = new Matrix();// 基准图片的matrix
	protected Matrix mSuppMatrix = new Matrix();// 进行额外操作时的matrix
	protected Matrix mDisplayMatrix = new Matrix();// 显示图片时的matrix
	private float[] mMatrixValues = new float[9];// 用来临时获得matrix的值的数组
	private float mMaxZoom;// 允许放大的最大倍数
	int mThisWidth = -1, mThisHeight = -1;// 当前绘制图片的宽度和高度
	private BoxMapImageDraw imageDraw;//用来画图片的类,主要是用来统一地图和缩略图的画法
	public BoxView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	//初始化的操作
	private void init(Context context){
		setScaleType(ImageView.ScaleType.MATRIX);
		imageDraw = new BoxMapImageDraw(context);
		clipWidth = imageDraw.getClipWidth();//每个碎片的高度
		clipHeight = imageDraw.getClipHeight();//每个碎片的高度
	}
	public BoxView(Context context) {
		super(context);
		init(context);
	}

	// 放大的操作
	protected void zoomIn(float rate) {
		if (getScale() >= mMaxZoom) {
			return;
		}
		if (mPushBoxImage == null) {
			return;
		}
		float width = getWidth();
		float height = getHeight();
		mSuppMatrix.postScale(rate, rate, width / 2F, height / 2F);
		setImageMatrix(getImageViewMatrix());
	}

	// 进行图片的缩小的操作
	protected void zoomOut(float rate) {
		if (mPushBoxImage == null) {
			return;
		}
		float width = getWidth();
		float height = getHeight();
		Matrix tmp = new Matrix(mSuppMatrix);
		mSuppMatrix.postScale(rate, rate, width / 2F, height / 2F);
		if (getScale(tmp) < 1f) {
			mSuppMatrix.setScale(1F, 1F, width / 2F, height / 2F);
		} else {
			mSuppMatrix.setScale(1f / rate, 1f / rate, width / 2F, height / 2f);
		}
		setImageMatrix(getImageViewMatrix());
		center(true, true);
	}

	/**
	 * 获得可以放大的最大倍数
	 * 
	 * @return
	 */
	protected float maxZoom() {
		if (mPushBoxImage == null) {
			return 1f;
		}
		float fw = (float) mPushBoxImage.getWidth() / (float) mThisWidth;
		float fh = (float) mPushBoxImage.getHeight() / (float) mThisHeight;
		float max = Math.max(fw, fh) * 4;
		return max;
	}

	protected float getScale() {
		return getScale(mSuppMatrix);
	}

	protected float getScale(Matrix matrix) {
		return getValue(matrix, Matrix.MSCALE_X);
	}

	protected float getValue(Matrix matrix, int whichValue) {
		matrix.getValues(mMatrixValues);
		return mMatrixValues[whichValue];
	}

	// 设置基本matrix,使bitmap好居中对齐
	private void setBaseMatrix(Bitmap bitmap, Matrix matrix) {
		if (bitmap == null) {
			return;
		}
		float viewWidth = getWidth();
		float viewHeight = getHeight();

		matrix.reset();
		float widthScale = Math
				.min(viewWidth / (float) bitmap.getWidth(), 1.0f);
		float heightScale = Math.min(viewHeight / (float) bitmap.getHeight(),
				1.0f);
		float scale;
		if (widthScale > heightScale) {
			scale = heightScale;
		} else {
			scale = widthScale;
		}
		matrix.setScale(scale, scale);
		matrix.postTranslate(
				(viewWidth - ((float) bitmap.getWidth() * scale)) / 2F,
				(viewHeight - ((float) bitmap.getHeight() * scale)) / 2F);
	}

	/**
	 * 获得当前需要显示的matrix
	 * 
	 * @return
	 */
	protected Matrix getImageViewMatrix() {
		mDisplayMatrix.set(mBaseMatrix);
		mDisplayMatrix.postConcat(mSuppMatrix);
		return mDisplayMatrix;
	}

	@Override
	public void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		mThisWidth = right - left;
		mThisHeight = bottom - top;
		if (mPushBoxImage == null) {
			generateBitmap();
		}
		setBaseMatrix(mPushBoxImage, mBaseMatrix);
		setImageMatrix(getImageViewMatrix());
		mMaxZoom = maxZoom();
	}

	// 把图片置于中间
	protected void center(boolean vertical, boolean horizontal) {
		if (mPushBoxImage == null)
			return;
		Matrix m = getImageViewMatrix();
		float[] topLeft = new float[] { 0, 0 };
		float[] botRight = new float[] { mPushBoxImage.getWidth(),
				mPushBoxImage.getHeight() };

		translatePoint(m, topLeft);
		translatePoint(m, botRight);
		float height = botRight[1] - topLeft[1];
		float width = botRight[0] - topLeft[0];
		float deltaX = 0, deltaY = 0;
		if (vertical) {
			int viewHeight = getHeight();
			if (height < viewHeight) {
				deltaY = (viewHeight - height) / 2 - topLeft[1];
			} else if (topLeft[1] > 0) {
				deltaY = -topLeft[1];
			} else if (botRight[1] < viewHeight) {
				deltaY = getHeight() - botRight[1];
			}
		}

		if (horizontal) {
			int viewWidth = getWidth();
			if (width < viewWidth) {
				deltaX = (viewWidth - width) / 2 - topLeft[0];
			} else if (topLeft[0] > 0) {
				deltaX = -topLeft[0];
			} else if (botRight[0] < viewWidth) {
				deltaX = viewWidth - botRight[0];
			}
		}
		postTranslate(deltaX, deltaY);// 进行移位的操作
		setImageMatrix(getImageViewMatrix());
	}

	/**
	 * 移位操作
	 * 
	 * @param dx
	 * @param dy
	 */
	protected void postTranslate(float dx, float dy) {
		mSuppMatrix.postTranslate(dx, dy);
	}

	private void translatePoint(Matrix matrix, float[] xy) {
		matrix.mapPoints(xy);
	}

	// 创建背景图片
	private void createBitmap() {
		if (mPushBoxImage != null) {
			mPushBoxImage.recycle();
		}
		float width = clipWidth * map.getWidth();
		float height = clipHeight * map.getHeight();
		mPushBoxImage = Bitmap.createBitmap((int) width, (int) height,
				Bitmap.Config.RGB_565);
	}

	// 绘制图片
	private void drawBitmap() {
		Canvas c1 = new Canvas(mPushBoxImage);
		for (int y = 0; y < map.getHeight(); y++) {
			for (int x = 0; x < map.getWidth(); x++) {
				drawEveryClip(c1, x, y);
			}
		}
	}

	// 绘制每个节点
	protected void drawEveryClip(Canvas c1, int x, int y) {
		imageDraw.drawEveryClip(c1,x,y,map);
	}

	/**
	 * 根据地图产生图片
	 */
	public void generateBitmap() {
		createBitmap();
		drawBitmap();
		super.setImageBitmap(mPushBoxImage);
	}

	protected Handler mHandler = new Handler();

	public Bitmap getmPushBoxImage() {
		return mPushBoxImage;
	}

	public void setmPushBoxImage(Bitmap mPushBoxImage) {
		this.mPushBoxImage = mPushBoxImage;
	}

	public Handler getmHandler() {
		return mHandler;
	}

	public void setmHandler(Handler mHandler) {
		this.mHandler = mHandler;
	}

	public float getClipWidth() {
		return clipWidth;
	}

	public void setClipWidth(float clipWidth) {
		this.clipWidth = clipWidth;
	}

	public float getClipHeight() {
		return clipHeight;
	}

	public void setClipHeight(float clipHeight) {
		this.clipHeight = clipHeight;
	}

	public Boxmap getMap() {
		return map;
	}

	public void setMap(Boxmap map) {
		this.map = map;
	}

	public Bitmap getBox() {
		return box;
	}

	public void setBox(Bitmap box) {
		this.box = box;
	}

	public Bitmap getWall() {
		return wall;
	}

	public void setWall(Bitmap wall) {
		this.wall = wall;
	}

	public Bitmap getBoxOnDest() {
		return boxOnDest;
	}

	public void setBoxOnDest(Bitmap boxOnDest) {
		this.boxOnDest = boxOnDest;
	}

	public Bitmap getManDown() {
		return manDown;
	}

	public void setManDown(Bitmap manDown) {
		this.manDown = manDown;
	}

	public Bitmap getManLeft() {
		return manLeft;
	}

	public void setManLeft(Bitmap manLeft) {
		this.manLeft = manLeft;
	}

	public Bitmap getManRight() {
		return manRight;
	}

	public void setManRight(Bitmap manRight) {
		this.manRight = manRight;
	}

	public Bitmap getManUp() {
		return manUp;
	}

	public void setManUp(Bitmap manUp) {
		this.manUp = manUp;
	}

	public Bitmap getBackGround() {
		return backGround;
	}

	public void setBackGround(Bitmap backGround) {
		this.backGround = backGround;
	}

	public Bitmap getDest() {
		return dest;
	}

	public void setDest(Bitmap dest) {
		this.dest = dest;
	}

	public float getShowWidth() {
		return showWidth;
	}

	public void setShowWidth(int showWidth) {
		this.showWidth = showWidth;
	}

	public float getShowHeight() {
		return showHeight;
	}

	public void setShowHeight(int showHeight) {
		this.showHeight = showHeight;
	}

}
